package org.jie.guider.java.base.threadpool;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * @author LiJie
 */
public class Metaspace_OOM {
    private static final String CLASS_DIR = "D:\\workspaces\\java\\dream\\guider\\";
    private static final String CLASS_NAME_PREFIX = "A_";

    private static Class<?> compile(String className, String code) throws URISyntaxException, IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        OOMClass oomClass = new OOMClass(className, code);
        List<OOMClass> classes = new ArrayList<OOMClass>(1) {{
            add(oomClass);
        }};
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, classes);
        boolean flag = task.call();
//        return flag ? load(className) : null;
        return null;
    }

    private static Class<?> load(String className) throws IOException {
        String classPath = CLASS_DIR + className + ".class";
        InputStream in = new FileInputStream(classPath);
        int len = in.available();
        byte[] code = new byte[len];
        in.read(code);
        MyClassLoader classLoader = new MyClassLoader();
        return classLoader.myDefineClass(className, code);
    }

    static class OOMClass extends SimpleJavaFileObject {
        private String code;

        public OOMClass(String name, String code) {
            super(URI.create("file:///" + name.replace(".", "/") + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }

    static class MyClassLoader extends ClassLoader {
        public Class<?> myDefineClass(String name, byte[] b) {
            return super.defineClass(name, b, 0, b.length);
        }
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, URISyntaxException {
        URL url = new File(CLASS_DIR).toURI().toURL();
        URL[] urls = {url};
        ClassLoader classLoader = new URLClassLoader(urls);
        int i = 1;
        int count = 100000;
        while (i < count) {
            String className = CLASS_NAME_PREFIX + i;
            String code = code(className);

            Class clz = compile(className, code);
            classLoader.loadClass(className);
            ++i;
        }
    }

    private static String code(String className) {
        return
            "import java.io.FileInputStream;\n" +
                "import java.io.IOException;\n" +
                "import java.io.InputStream;\n" +
                "import java.net.URI;\n" +
                "import java.net.URISyntaxException;\n" +
                "import java.util.ArrayList;\n" +
                "import java.util.List;\n" +
                "import javax.tools.JavaCompiler;\n" +
                "import javax.tools.SimpleJavaFileObject;\n" +
                "import javax.tools.StandardJavaFileManager;\n" +
                "import javax.tools.ToolProvider;\n" +
                "\n" +
                "/**\n" +
                " * @author LiJie\n" +
                " */\n" +
                "public class " + className + " {\n" +
                "    private static final String CLASS_DIR = \"D:\\\\workspaces\\\\java\\\\dream\\\\guider\\\\\";\n" +
                "    private static final String CLASS_NAME_PREFIX = \"A_\";\n" +
                "\n" +
                "    private static Class<?> compile(String className, String code) throws URISyntaxException, IOException {\n" +
                "        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();\n" +
                "        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);\n" +
                "        OOMClass oomClass = new OOMClass(className, code);\n" +
                "        List<OOMClass> classes = new ArrayList<OOMClass>(1) {{\n" +
                "            add(oomClass);\n" +
                "        }};\n" +
                "        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, classes);\n" +
                "        boolean flag = task.call();\n" +
                "        return flag ? load(className) : null;\n" +
                "    }\n" +
                "\n" +
                "    private static Class<?> load(String className) throws IOException {\n" +
                "        String classPath = CLASS_DIR + className + \".class\";\n" +
                "        InputStream in = new FileInputStream(classPath);\n" +
                "        int len = in.available();\n" +
                "        byte[] code = new byte[len];\n" +
                "        in.read(code);\n" +
                "        MyClassLoader classLoader = new MyClassLoader();\n" +
                "        return classLoader.myDefineClass(className, code);\n" +
                "    }\n" +
                "\n" +
                "    static class OOMClass extends SimpleJavaFileObject {\n" +
                "        private String code;\n" +
                "\n" +
                "        public OOMClass(String name, String code) {\n" +
                "            super(URI.create(\"file:///\" + name.replace(\".\", \"/\") + Kind.SOURCE.extension), Kind.SOURCE);\n" +
                "            this.code = code;\n" +
                "        }\n" +
                "\n" +
                "        @Override\n" +
                "        public CharSequence getCharContent(boolean ignoreEncodingErrors) {\n" +
                "            return code;\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    static class MyClassLoader extends ClassLoader {\n" +
                "        public Class<?> myDefineClass(String name, byte[] b) {\n" +
                "            return super.defineClass(name, b, 0, b.length);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public static void main(String[] args) throws URISyntaxException, IOException {\n" +
                "        int count = 100000;\n" +
                "        int x = 1;\n" +
                "        while (x < count) {\n" +
                "            String className = CLASS_NAME_PREFIX + x;\n" +
                "            StringBuilder code = new StringBuilder();\n" +
                "            code.append(\"public class \").append(className).append(\"{\");\n" +
                "\n" +
                "            code.append(\"public void b() {}\");\n" +
                "\n" +
                "            code.append(\"}\");\n" +
                "\n" +
                "            Class clz = compile(className, code.toString());\n" +
                "            System.out.println(clz.getSimpleName());\n" +
                "            ++x;\n" +
                "        }\n" +
                "    }\n" +
                "}\n";
    }
}
